package com.qinglei.recoup.system.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qinglei.recoup.common.domain.ActiveUser;
import com.qinglei.recoup.common.service.CacheService;
import com.qinglei.recoup.common.tenant.TenantContextHolder;
import com.qinglei.recoup.common.utils.RecoupUtil;
import com.qinglei.recoup.system.dao.EventProcMapper;
import com.qinglei.recoup.system.domain.*;
import com.qinglei.recoup.system.enums.EventEnum;
import com.qinglei.recoup.system.pojo.AlarmEventItemVO;
import com.qinglei.recoup.system.pojo.BatchProcEventParam;
import com.qinglei.recoup.system.pojo.BedStatusVO;
import com.qinglei.recoup.system.pojo.EventItemVO;
import com.qinglei.recoup.system.pojo.EventProcItemDTO;
import com.qinglei.recoup.system.pojo.EventProcParam;
import com.qinglei.recoup.system.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author syp
 */
@Service
@Slf4j
public class EventProcServiceImpl extends ServiceImpl<EventProcMapper, EventProc> implements EventProcService {

    @Autowired
    private BedService bedService;

    @Autowired
    @Lazy
    private EventLogService eventLogService;

    @Autowired
    private NurseBedService nurseBedService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private EventService eventService;

    @Autowired
    private EventWsService eventWsService;
    /**
     * 获取事件统计数据
     *
     * @param userId
     * @param day
     * @return
     */
    @Override
    public EventStatisticsBO getEventProcDataByDay(Long userId, LocalDate day) {
        // 接单数据
        List<EventProc> eventProcTake = this.baseMapper.selectList(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getTakeUserId, userId)
                .between(EventProc::getTakeTime, day, day.plusDays(1)));
        if (CollectionUtils.isEmpty(eventProcTake)) {
            return null;
        }
        int eventTakeTimes = 0;
        int eventWaitTime = 0;
        for (EventProc eventProc : eventProcTake) {
            eventWaitTime += Duration.between(eventProc.getHappenTime(), eventProc.getTakeTime()).toMillis() / 1000;
            eventTakeTimes++;
        }
        List<EventProc> eventProcsFinish = this.baseMapper.selectList(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getFinishUserId, userId)
                .between(EventProc::getFinishTime, day, day.plusDays(1))
                .isNull(EventProc::getEndTime));
        if (CollectionUtils.isEmpty(eventProcsFinish)) {
            return null;
        }
        int eventFinishTimes = 0;
        int eventProcTime = 0;
        for (EventProc eventProc : eventProcsFinish) {
            eventProcTime += Duration.between(eventProc.getTakeTime(), eventProc.getFinishTime()).toMillis() / 1000;
            eventFinishTimes++;
        }
        EventStatisticsBO eventStatisticsBO = null;
        if (eventTakeTimes > 0 || eventFinishTimes > 0) {
            eventStatisticsBO = new EventStatisticsBO();
        }
        if (eventTakeTimes > 0) {
            eventStatisticsBO.setEventTakeTimes(eventTakeTimes);
            eventStatisticsBO.setEventWaitTime(eventWaitTime);
        }
        if (eventFinishTimes > 0) {
            eventStatisticsBO.setEventFinishTimes(eventFinishTimes);
            eventStatisticsBO.setEventProcTime(eventProcTime);
        }
        return eventStatisticsBO;
    }

    /**
     * 获取用户今天漏掉异常
     *
     * @param userId
     * @param today
     * @return
     */
    @Override
    public Integer getMissAbnormalCnt(Long userId, LocalDate today) {
        return baseMapper.selectCount(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getTakeUserId, userId)
                .eq(EventProc::getStatus, 4).ge(EventProc::getEndTime, today));
    }

    /**
     * 获取用户历史漏掉异常
     *
     * @param userId
     * @param startDay
     * @param yesterday
     * @return
     */
    @Override
    public Integer getMissAbnormalHistory(Long userId, LocalDate startDay, LocalDate yesterday) {
        // LocalDateTime转字符串(true)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String format = yesterday.format(formatter);

        // 将时间字符串转自定义格式的LocalDateTime
        String timeString = format + " 23:59:59";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime parse = LocalDateTime.parse(timeString, dateTimeFormatter);

        return baseMapper.selectCount(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getTakeUserId, userId)
                .eq(EventProc::getStatus, 4)
                .ge(EventProc::getEndTime, startDay)
                .le(EventProc::getEndTime, parse));
    }

    @Override
    public Integer getMissedEventNumByBed(Integer bedId) {
        LocalDateTime time = LocalDateTime.now().minusDays(7);
        Integer num = baseMapper.selectCount(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getBedId, bedId)
                .eq(EventProc::getStatus, EventProc.STATUS_MISSED)
                .ge(EventProc::getEndTime, time));
        return num;
    }

    @Override
    public List<EventItemVO> getMissedEventListByBed(Integer bedId) {
        LocalDateTime time = LocalDateTime.now().minusDays(7);
        List<EventProc> eventProcs = baseMapper.selectList(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getBedId, bedId)
                .eq(EventProc::getStatus, EventProc.STATUS_MISSED)
                .ge(EventProc::getEndTime, time)
                .orderByDesc(EventProc::getEndTime));
        final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM-dd");
        final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
        List<EventItemVO> eventItemVOList = eventProcs.stream().map(item -> {
            Event event = eventService.getById(item.getEventId());
            EventItemVO eventItemVO = new EventItemVO();
            eventItemVO.setEventId(item.getEventId());
            if (event.getType() == Bed.SUB_STATUS_FALL_DOWN) {
                eventItemVO.setEventName("跌倒");
            } else  if (event.getType() == Bed.SUB_STATUS_OUT_BED_TOO_LONG){
                eventItemVO.setEventName("离床过久");
            } else  if (event.getType() == BedHealthState.SUB_STATUS_BREATH_LOW){
                eventItemVO.setEventName("呼吸暂停过久");
            } else  if (event.getType() == BedHealthState.SUB_STATUS_BREATH_HIGHT){
                eventItemVO.setEventName("呼吸过速");
            } else  if (event.getType() == BedHealthState.SUB_STATUS_HEART_LOW){
                eventItemVO.setEventName("心跳过缓");
            }else  if (event.getType() == BedHealthState.SUB_STATUS_HEART_HIGHT){
                eventItemVO.setEventName("心跳过速");
            }



//            else {
//                eventItemVO.setEventName("离床过久");
//            }
            eventItemVO.setDate(item.getHappenTime().format(dateFormatter));
            String start = item.getHappenTime().format(timeFormatter);
            String end = item.getEndTime().format(timeFormatter);
            eventItemVO.setTime(start + "-" + end);
            return eventItemVO;
        }).collect(Collectors.toList());
        return eventItemVOList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean procAlarmEvent(EventProcParam param) {
        LocalDateTime now = LocalDateTime.now();
        User user = RecoupUtil.getCurrentUser();
        if(!"3".equals(user.getRoleId())){
            log.error("role not procAlarm  ", user);
            return false;
        }
        //TODO 需要前端分情况处理，如果是离床传递 eventId  如果是呼吸心跳BPM需要传递 healthEventId
        //TODO 由于时间的关系，本功能暂时不和前端调试  ，等呼吸心跳长时间测试结果出来需要和前端调试。     ----董强华
        Integer bedId = param.getBedId();
        Bed bed = bedService.getById(bedId);
        if (bed == null) {
            log.error("procAlarmBatch bed not exist bedId:{}", bedId);
            return false;
        }
        Long eventId = param.getEventId();
        Event event = eventService.getById(eventId);
        if(event.getType()<=2){
            if (bed.getStatus() != Bed.STATUS_ALARM || bed.getEventId() == null) {
                log.warn("procAlarmBatch bed status not correct bed:{}", bed);
                return true;
            }
        }else{
            if ( bed.getEventId() == null) {
                log.warn("procAlarmBatch bed status not correct bed:{}", bed);
                return true;
            }
        }
        EventProc eventProc = baseMapper.selectOne(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getEventId, eventId));
        if (eventProc == null) {
            log.error("procAlarmBatch eventProc not exist eventId:{}", eventId);
            return false;
        }
        if (eventProc.getStatus() == EventProc.STATUS_FINISHED || eventProc.getStatus() == EventProc.STATUS_MISSED) {
            return true;
        }

        long duration = Duration.between(eventProc.getHappenTime(), now).toMillis() / 1000;

        EventProc eventProcUpdate = new EventProc();
        eventProcUpdate.setId(eventProc.getId());
        eventProcUpdate.setStatus(EventProc.STATUS_FINISHED);
        if (eventProc.getStatus() == EventProc.STATUS_HAPPEN) {
            eventProcUpdate.setTakeTime(now);
            eventProcUpdate.setWaitTime((int) duration);
            eventProcUpdate.setTakeUserId(user.getId());
        }

        eventProcUpdate.setFinishTime(now);
        eventProcUpdate.setProcTime((int) duration);
        eventProcUpdate.setFinishUserId(user.getId());
        eventProcUpdate.setSiteOption(EventProc.OPTION_OTHER);
        eventProcUpdate.setProcReport("");

        baseMapper.updateById(eventProcUpdate);

        // 记录事件日志并推送消息
        eventLogService.procEventLog(bedId, eventProcUpdate.getFinishUserId(), eventId,
                EventEnum.EVENT_PROC_FINISH.getCode(), eventProcUpdate.getProcReport(), bed.getTenantId());
        // 推送床位当前状态消息
        if(event!=null && event.getType()>2){
            BedStatusVO bedStatusVO = bedService.getBedStatusById(bedId);
            switch(event.getType()){
                case 3 :
                    bedStatusVO.setStatusName("呼吸正常");
                    break;
                case 4 :
                    bedStatusVO.setStatusName("呼吸过速");
                    break;
                case 5 :
                    bedStatusVO.setStatusName("呼吸暂停过久");
                    break;
                case 6 :
                    bedStatusVO.setStatusName("心跳正常");
                    break;
                case 7 :
                    bedStatusVO.setStatusName("心跳过速");
                    break;
                case 8 :
                    bedStatusVO.setStatusName("心跳过缓");
                    break;
                default :
            }
            bedStatusVO.setStatus(6);
            bedStatusVO.setSubStatus(3);
            eventWsService.pushBedStatusMsg(bedStatusVO);
        }else {
            bedService.pushBedCurStatus(bedId);
        }

        return true;
    }

    @Override
    public void procMissedEvent(BatchProcEventParam param) {
        LocalDateTime now = LocalDateTime.now();
        User user = RecoupUtil.getCurrentUser();
        for (Long eventId : param.getEventIds()) {
            EventProc eventProc = baseMapper.selectOne(Wrappers.<EventProc>lambdaQuery()
                    .eq(EventProc::getEventId, eventId));
            if (eventProc == null) {
                log.warn("procAlarmBatch eventProc not exist eventId:{}", eventId);
                continue;
            }
            if (eventProc.getStatus() == EventProc.STATUS_FINISHED) {
                continue;
            }

            long duration = Duration.between(eventProc.getHappenTime(), now).toMillis() / 1000;

            EventProc eventProcUpdate = new EventProc();
            eventProcUpdate.setId(eventProc.getId());
            eventProcUpdate.setStatus(EventProc.STATUS_FINISHED);

            eventProcUpdate.setFinishTime(now);
            eventProcUpdate.setProcTime((int) duration);
            eventProcUpdate.setFinishUserId(user.getId());
            eventProcUpdate.setSiteOption(EventProc.OPTION_OTHER);
            eventProcUpdate.setProcReport(param.getProcReport());

            baseMapper.updateById(eventProcUpdate);
        }
        // 记录事件日志并推送消息
        eventLogService.procEventLog(param.getBedId(), user.getId(), null,
                EventEnum.EVENT_PROC_PROC_MISSED_EVENT.getCode(),
                String.format(EventEnum.EVENT_PROC_PROC_MISSED_EVENT.getContent(), user.getName(),
                        param.getEventIds().size(), param.getProcReport()), TenantContextHolder.getTenantId());
        // 推送床位当前状态消息
        bedService.pushBedCurStatus(param.getBedId(), false);
    }

    /**
     * 将正在发生的事件处理成漏掉的事件
     * @param bedId
     */
    @Override
    @Async
    public void procEvent2MissedStatus4Hmp(Integer bedId) {
        try {
            List<Long> eventIds = baseMapper.getHappeningEventIds4Hmp(bedId, Arrays.asList(4, 5, 7, 8));
            if (CollectionUtils.isEmpty(eventIds)) {
                return;
            }
            log.info("procEvent2MissedStatus4Hmp bedId:{},eventIds:{}", bedId, eventIds);
            for (Long eventId : eventIds) {
                this.saveMissedEvent(eventId);
            }
        } catch (Exception e) {
            log.error("procEvent2MissedStatus4Hmp error", e);
        }
    }

    @Override
    public void saveMissedEvent(Long eventId) {
        EventProc eventProc = baseMapper.selectOne(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getEventId, eventId));
        if (eventProc == null) {
            log.warn("procAlarmBatch eventProc not exist eventId:{}", eventId);
            return;
        }
        if (eventProc.getStatus() == EventProc.STATUS_FINISHED || eventProc.getStatus() == EventProc.STATUS_MISSED) {
            return;
        }
        EventProc eventProcUpdate = new EventProc();
        eventProcUpdate.setId(eventProc.getId());
        eventProcUpdate.setStatus(EventProc.STATUS_MISSED);
        LocalDateTime now = LocalDateTime.now();
        eventProcUpdate.setEndTime(now);
        baseMapper.updateById(eventProcUpdate);

        // 如果护工有错过的没有处理的任务，一个是要在“工作日志”内有记录
        this.saveNurseMissedLog(eventProc.getBedId(), eventProc.getEventId(), eventProc.getTenantId());
    }

    @Override
    public void saveNurseMissedLog(Integer bedId, Long eventId, Integer tenantId) {
        try {
            List<ActiveUser> onlineNurse = cacheService.getOnlineNurse();
            final Set<Long> onlineNurseIds = onlineNurse.stream().map(ActiveUser::getUserId).collect(Collectors.toSet());
            Set<Long> nurseIds = nurseBedService.getNurseIdsByBed(bedId);
            List<Long> a = new ArrayList<>();
            a.addAll(onlineNurseIds);
            List<Long> b = new ArrayList<>();
            b.addAll(nurseIds);
            Collection<Long> intersection = CollectionUtils.intersection(a, b);
            for (Long nurseId : intersection) {
                // 记录事件日志并推送消息
                eventLogService.procEventLog(bedId, nurseId, eventId,
                        EventEnum.EVENT_PROC_HAPPEN_MISSED_EVENT.getCode(), null, tenantId);
            }
        } catch (Exception e) {
            log.error("saveNurseMissedLog error", e);
        }

    }

    /**
     * 获取报警数量
     *
     * @param bedId
     * @return
     */
    @Override
    public Integer getAlarmEventCount(Integer bedId) {
        Integer alarmEventCount = baseMapper.getAlarmEventCount(bedId, null, null, Arrays.asList(4, 5, 7, 8));
        return alarmEventCount;
    }

    /**
     * 获取报警信息列表
     *
     * @param startTime
     * @param bedId
     * @param endTime
     * @param pageParam
     * @return
     */
    @Override
    public IPage<AlarmEventItemVO> pageAlarmEventList(Integer bedId, LocalDateTime startTime, LocalDateTime endTime, IPage pageParam) {
        IPage<AlarmEventItemVO> retPage = new Page<>();
        IPage<EventProcItemDTO> eventPage = baseMapper.pageAlarmEventList(pageParam, bedId,
                startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                        Arrays.asList(4, 5, 7, 8));
        if (eventPage == null) {
            return retPage;
        }
        BeanUtils.copyProperties(eventPage, retPage);
        final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM-dd");
        final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
        if (eventPage.getSize() > 0) {
            List<AlarmEventItemVO> alarmEventList = eventPage.getRecords().stream().map(item -> {
                AlarmEventItemVO eventItemVO = new AlarmEventItemVO();
                eventItemVO.setEventId(item.getEventId());
                if (item.getType() == BedHealthState.SUB_STATUS_BREATH_LOW) {
                    eventItemVO.setEventName("呼吸暂停过久");
                } else if (item.getType() == BedHealthState.SUB_STATUS_BREATH_HIGHT) {
                    eventItemVO.setEventName("呼吸过速");
                } else if (item.getType() == BedHealthState.SUB_STATUS_HEART_LOW) {
                    eventItemVO.setEventName("心跳过缓");
                } else if (item.getType() == BedHealthState.SUB_STATUS_HEART_HIGHT) {
                    eventItemVO.setEventName("心跳过速");
                } else {
                    eventItemVO.setEventName("不支持");
                }
                eventItemVO.setType(item.getType());
                eventItemVO.setDate(item.getHappenTime().format(dateFormatter));
                String start = item.getHappenTime().format(timeFormatter);
                LocalDateTime tmpEndTime = null;
                if (item.getEndTime() != null) {
                    tmpEndTime = item.getEndTime();
                } else if (item.getFinishTime() != null) {
                    tmpEndTime = item.getFinishTime();
                }
                String end = tmpEndTime != null ? tmpEndTime.format(timeFormatter) : "";
                eventItemVO.setTime(start + "-" + end);
                if (item.getStatus() == EventProc.STATUS_FINISHED) {
                    eventItemVO.setIsRead(1);
                } else {
                    eventItemVO.setIsRead(0);
                }
                return eventItemVO;
            }).collect(Collectors.toList());
            retPage.setRecords(alarmEventList);
        }
        return retPage;
    }

    /**
     * 将报警时间置为完成
     *
     * @param userId
     * @param tenantId
     * @param bedId
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Boolean finishAlarmEvent(Long userId, Integer tenantId, Integer bedId, LocalDateTime startTime, LocalDateTime endTime) {
        log.info("finishAlarmEvent userId:{},tenantId:{},bedId:{},startTime:{},endTime:{}", userId, tenantId, bedId, startTime, endTime);
        List<EventProc> eventProcs = baseMapper.selectList(Wrappers.<EventProc>lambdaQuery()
                .eq(EventProc::getBedId, bedId)
                .in(EventProc::getStatus, Arrays.asList(EventProc.STATUS_MISSED,EventProc.STATUS_HAPPEN,EventProc.STATUS_PROCESSING))
                .ge(EventProc::getHappenTime, startTime)
                .lt(EventProc::getHappenTime, endTime));
        log.info("finishAlarmEvent eventProcs;{}", eventProcs);
        if (CollectionUtils.isEmpty(eventProcs)) {
            return true;
        }
        LocalDateTime now = LocalDateTime.now();
        for (EventProc eventProc : eventProcs) {
            long duration = Duration.between(eventProc.getHappenTime(), now).toMillis() / 1000;
            EventProc eventProcUpdate = new EventProc();
            eventProcUpdate.setId(eventProc.getId());
            eventProcUpdate.setStatus(EventProc.STATUS_FINISHED);
            eventProcUpdate.setFinishTime(now);
            eventProcUpdate.setProcTime((int) duration);
            eventProcUpdate.setFinishUserId(userId);
            eventProcUpdate.setSiteOption(EventProc.OPTION_OTHER);
            eventProcUpdate.setProcReport("");
            baseMapper.updateById(eventProcUpdate);
        }
        return true;
    }

    public static void main(String[] args) {
        LocalDate date = LocalDate.parse("2022-01" + "-01");
        System.out.println(date);
        LocalTime time = LocalTime.parse("00:00:00");
        LocalDateTime startTime = LocalDateTime.of(date, time);
        System.out.println(startTime);
    }

//    public static void main(String[] args) {
//        Set <Long> onlineNurseIds = new HashSet<>(1);
//            onlineNurseIds.add(144l);
//        final Set <Long> nurseIds = new HashSet<>(2);
//            nurseIds.add(144l);
//            nurseIds.add(145l);
//        Collection<Long> intersection = CollectionUtils.intersection(onlineNurseIds, nurseIds);
//        System.out.println(intersection);
//    }

}
